#pragma once

#include "Shape.h"

class Line: public Shape {
private:
	/*
	Default constructor, avoid using this.
	*/
	Line();

	Vector2D* dir; // direction
	float dist; // distance, negative means infinite

	Vector2D* normal; // normal (perpendicular)
	Vector2D* alterNormal; // alternative normal (perpendicular)

	/*
	Calculate the normals of the Line Segment.
	*/
	void CalculateNormals();

	/*
	Clean up the dir.
	*/
	void CleanUpDir();
	/*
	Clean up the normal.
	*/
	void CleanUpNormal();
	/*
	Clean up the alternate normal.
	*/
	void CleanUpAlterNormal();

public:
	/*
	Create an infinite Line that follows the given Transform and goes in the given direction.
	@param	position		Transform to follow.
	@param	direction		Direction of the line.
	*/
	Line(Transform* const position, const Vector2D& direction);
	/*
	Create an infinite Line that follows the given Transform and goes in the given direction.
	@param	position		Transform to follow.
	@param	posOffset		Offset to apply to the Transform being followed.
	@param	direction		Direction of the line.
	*/
	Line(Transform* const position, const Vector2D& posOffset, const Vector2D& direction);
	/*
	Create an infinite Line that follows the given Transform and goes in the given direction.
	@param	position		Transform to follow.
	@param	xOffset			Offset in x to apply to the Transform being followed.
	@param	yOffset			Offset in y to apply to the Transform being followed.
	@param	direction		Direction of the line.
	*/
	Line(Transform* const position, float xOffset, float yOffset, const Vector2D& direction);
	/*
	Create an infinite Line that follows the given Transform and goes in the given direction.
	@param	position		Transform to follow.
	@param	posOffset		Offset to apply to the Transform being followed.
	@param	xDirection		Direction in x of the line.
	@param	yDirection		Direction in y of the line.
	*/
	Line(Transform* const position, const Vector2D& posOffset, float xDirection, float yDirection);
	/*
	Create an infinite Line that follows the given Transform and goes in the given direction.
	@param	position		Transform to follow.
	@param	xOffset			Offset in x to apply to the Transform being followed.
	@param	yOffset			Offset in y to apply to the Transform being followed.
	@param	xDirection		Direction in x of the line.
	@param	yDirection		Direction in y of the line.
	*/
	Line(Transform* const position, float xOffset, float yOffset, float xDirection, float yDirection);
	/*
	Create a finite Line that follows the given Transform and goes in the given direction.
	@param	position		Transform to follow.
	@param	direction		Direction of the line.
	@param	distance		Length of the line (negative for infinite).
	*/
	Line(Transform* const position, const Vector2D& direction, float distance);
	/*
	Create a finite Line that follows the given Transform and goes in the given direction.
	@param	position		Transform to follow.
	@param	posOffset		Offset to apply to the Transform being followed.
	@param	direction		Direction of the line.
	@param	distance		Length of the line (negative for infinite).
	*/
	Line(Transform* const position, const Vector2D& posOffset, const Vector2D& direction, float distance);
	/*
	Create a finite Line that follows the given Transform and goes in the given direction.
	@param	position		Transform to follow.
	@param	xOffset			Offset in x to apply to the Transform being followed.
	@param	yOffset			Offset in y to apply to the Transform being followed.
	@param	direction		Direction of the line.
	@param	distance		Length of the line (negative for infinite).
	*/
	Line(Transform* const position, float xOffset, float yOffset, const Vector2D& direction, float distance);
	/*
	Create a finite Line that follows the given Transform and goes in the given direction.
	@param	position		Transform to follow.
	@param	posOffset		Offset to apply to the Transform being followed.
	@param	xDirection		Direction in x of the line.
	@param	yDirection		Direction in y of the line.
	@param	distance		Length of the line (negative for infinite).
	*/
	Line(Transform* const position, const Vector2D& posOffset, float xDirection, float yDirection, float distance);
	/*
	Create a finite Line that follows the given Transform and goes in the given direction.
	@param	position		Transform to follow.
	@param	xOffset			Offset in x to apply to the Transform being followed.
	@param	yOffset			Offset in y to apply to the Transform being followed.
	@param	xDirection		Direction in x of the line.
	@param	yDirection		Direction in y of the line.
	@param	distance		Length of the line (negative for infinite).
	*/
	Line(Transform* const position, float xOffset, float yOffset, float xDirection, float yDirection, float distance);
	/*
	Destructor.
	*/
	~Line();

	/*
	Get the direction of the line.
	@param	out				Vector to store the data to.
	*/
	void GetDirection(Vector2D& out) const { out.x = dir->x; out.y = dir->y; return; }
	/*
	@return	Direction of the line in x.
	*/
	float GetDirectionX() const { return dir->x; }
	/*
	@return	Direction of the line in y.
	*/
	float GetDirectionY() const { return dir->y; }
	/*
	@return	Length of the line (negative means infinite).
	*/
	float GetDistance() const { return dist; }
	/*
	@return	Length of the line (negative means infinite).
	*/
	float GetLength() const { return dist; }
	/*
	Get the normal of the line.
	@param	out				Vector to store the data to.
	*/
	void GetNormal(Vector2D& out) const { out.x = normal->x; out.y = normal->y; return; }
	/*
	Get the alternate normal of the line.
	@param	out				Vector to store the data to.
	*/
	void GetAlternateNormal(Vector2D& out) const { out.x = alterNormal->x; out.y = alterNormal->y; return; }
	/*
	Set the direction of the line.
	@param	xDirection		Direction of the line in x.
	@param	yDirection		Direction of the line in y.
	*/
	void SetDirection(float xDirection, float yDirection);
	/*
	Set the direction of the line.
	@param	direction		Direction of the line.
	*/
	void SetDirection(const Vector2D& direction);
	/*
	Set the length of the line.
	@param	distance		Length of the line (negative for infinite).
	*/
	void SetDistance(float distance) { dist = distance; return; }
	/*
	Set the length of the line.
	@param	distance		Length of the line (negative for infinite).
	*/
	void SetLength(float distance) { dist = distance; return; }
	/*
	Add to the length of the line.
	@param	addDistance		Amount to add to the length of the line.
	*/
	void Extend(float addDistance) { dist += addDistance; return; }

	/*
	Get the point on the edge of the circle that points toward the given line.
	@param	c				Circle to check the collision point with.
	@param	pOut			Vector to store the data to.
	*/
	void GetCollisionPoint(const Circ* const c, Vector2D& pOut) const;

	/*
	Calculate the slope of the line.
	@return	Slope of the line.
	*/
	float CalculateSlope() const;
	/*
	Calculate the intercept of the line.
	@return	Intercept of the line.
	*/
	float CalculateIntercept() const;
	/*
	Calculate the discriminant of a given quadratic equation.
	@param	a			Parameter of the equation in the form (ax^2 + bx + c = 0).
	@param	b			Parameter of the equation in the form (ax^2 + bx + c = 0).
	@param	c			Parameter of the equation in the form (ax^2 + bx + c = 0).
	@return	Discriminant of the given quadratic equation.
	*/
	float CalculateDiscriminant(float a, float b, float c) const;
	/*
	Check if the line is a vertical line or not.
	@return	Wether or not the line is vertical.
	*/
	bool IsVerticalLine() const;
	/*
	Check if the line is a horizontal line or not.
	@return	Wether or not the line is horizontal.
	*/
	bool IsHorizontalLine() const;
	/*
	Check if the line is null or not.
	@return	Wether or not the line is null (has no direction or distance).
	*/
	bool IsNullLine() const;

	/*
	Check if a given point is on the line.
	@param	x			Position of the point in x.
	@param	y			Position of the point in y.
	@return	Wether the point is on the line or not.
	*/
	bool Contains(float x, float y) const;
	/*
	Check if a given point is on the line.
	@param	p			Position of the point.
	@return	Wether the point is on the line or not.
	*/
	bool Contains(const Vector2D& p) const;
	/*
	Check if there is a collision with the given Shape.
	@param	l			Shape to check the collision with.
	@param	hitOut		Vector to store the hit location to in case of collision.
	@return	Wether the two objects collide or not.
	*/
	bool CollidesWith(const Line* const l, Vector2D& hitOut) const;
	/*
	Check if there is a collision with the given Shape.
	@param	r			Shape to check the collision with.
	@param	hitOut		Vector to store the hit location to in case of collision.
	@return	Wether the two objects collide or not.
	*/
	bool CollidesWith(const Rect* const r, Vector2D& hitOut) const;
	/*
	Check if there is a collision with the given Shape.
	@param	c			Shape to check the collision with.
	@param	hitOut		Vector to store the hit location to in case of collision.
	@return	Wether the two objects collide or not.
	*/
	bool CollidesWith(const Circ* const c, Vector2D& hitOut) const;
	/*
	Check if there is a collision with the given Shape.
	@param	p			Shape to check the collision with.
	@param	hitOut		Vector to store the hit location to in case of collision.
	@return	Wether the two objects collide or not.
	*/
	bool CollidesWith(const Poly* const p, Vector2D& hitOut) const;

	/*
	DO NOT USE THIS METHOD.
	*/
	void Draw() const;
	/*
	Display the rectangle information in the console.
	*/
	void COut() const;
};

